package main

import (
	"bytes"
	myErr "cogpt/internal/cogpterror"
	"cogpt/internal/proxy"
	"encoding/json"
	"net/http"
	"strings"
)

var proxyAddress string

type LoginInfo struct {
	Interval         int    `json:"interval"`
	Verification_uri string `json:"verification_uri"`
	User_code        string `json:"user_code"`
	Device_code      string `json:"device_code"`
}

type AuthInfo struct {
	Error        string `json:"error"`
	Access_token string `json:"access_token"`
}

var HEADERS map[string][]string = map[string][]string{
	"Accept":       {"application/json"},
	"Content-Type": {"application/json"},
}

func getLoginInfo() (*LoginInfo, myErr.Error) {
	var err error
	url := "https://github.com/login/device/code"
	body := map[string]string{
		"client_id": "Iv1.b507a08c87ecfe98",
		"scope":     "read:user",
	}

	requestBodyJSON, err := json.Marshal(body)
	if err != nil {
		return nil, &myErr.CoErr{ErrCode: myErr.ERR_MARSHAL_JSON, Msg: err.Error() + "\nin getLoginInfo()"}
	}

	// create http client and request
	client, err := proxy.GenProxyClient(proxyAddress, 10)
	if err != nil {
		return nil, err.(*myErr.CoErr)
	}
	req, err := http.NewRequest("POST", url, bytes.NewReader(requestBodyJSON))
	if err != nil {
		return nil, &myErr.CoErr{ErrCode: myErr.ERR_FAILED_TO_CREATE_HTTP_CLIENT, Msg: err.Error(), Typ: "GetLoginInfo"}
	}
	// set headers
	req.Header = HEADERS

	// send request
	resp, err := client.Do(req)
	if err != nil {
		if strings.Contains(err.Error(), "Client.Timeout") {
			return nil, &myErr.CoErr{ErrCode: myErr.ERR_REQUEST_TIMEOUT, Msg: err.Error(), Typ: "GetLoginInfo"}
		} else {
			return nil, &myErr.CoErr{ErrCode: myErr.ERR_UNKNOWN, Msg: err.Error(), Typ: "GetLoginInfo"}
		}
	}
	defer resp.Body.Close()

	var loginInfo LoginInfo
	// read response body
	err = json.NewDecoder(resp.Body).Decode(&loginInfo)
	if err != nil {
		return nil, &myErr.CoErr{ErrCode: myErr.ERR_FAILED_TO_PARSE_JSON, Msg: err.Error(), Typ: "GetLoginInfo"}
	}

	return &loginInfo, nil
}

func pollAuth(device_code string) (string, *myErr.CoErr) {
	var err error
	url := "https://github.com/login/oauth/access_token"

	body := map[string]string{
		"client_id":   "Iv1.b507a08c87ecfe98",
		"device_code": device_code,
		"grant_type":  "urn:ietf:params:oauth:grant-type:device_code",
	}

	requestBodyJSON, err := json.Marshal(body)
	if err != nil {
		return "", &myErr.CoErr{ErrCode: myErr.ERR_MARSHAL_JSON, Msg: err.Error() + "\nin pollAuth()"}
	}

	// create http client and request
	client, err := proxy.GenProxyClient(proxyAddress, 10)
	if err != nil {
		return "", err.(*myErr.CoErr)
	}
	req, err := http.NewRequest("POST", url, bytes.NewReader(requestBodyJSON))
	if err != nil {
		return "", &myErr.CoErr{ErrCode: myErr.ERR_FAILED_TO_CREATE_HTTP_CLIENT, Msg: err.Error() + "\nin pollAuth()"}
	}
	req.Header = HEADERS

	resp, err := client.Do(req)
	if err != nil {
		if strings.Contains(err.Error(), "Client.Timeout") {
			return "", &myErr.CoErr{ErrCode: myErr.ERR_REQUEST_TIMEOUT, Msg: err.Error() + "\nin pollAuth()"}
		} else {
			return "", &myErr.CoErr{ErrCode: myErr.ERR_UNKNOWN, Msg: err.Error() + "\nin pollAuth()"}
		}
	}
	defer resp.Body.Close()

	var authInfo AuthInfo
	// read response body
	err = json.NewDecoder(resp.Body).Decode(&authInfo)
	if err != nil {
		return "", &myErr.CoErr{ErrCode: myErr.ERR_FAILED_TO_PARSE_JSON, Msg: err.Error() + "\nin pollAuth()"}
	}

	if authInfo.Error == "authorization_pending" {
		return "", &myErr.CoErr{ErrCode: myErr.ERR_AUTH_PENDING, Msg: "Authorization pending"}
	} else if authInfo.Error == "expired_token" {
		return "", &myErr.CoErr{ErrCode: myErr.ERR_EXPIRED_TOKEN, Msg: "Expired token"}
	} else if authInfo.Access_token != "" {
		return authInfo.Access_token, nil
	} else {
		return "", &myErr.CoErr{ErrCode: myErr.ERR_UNKNOWN, Msg: "Unknown error"}
	}
}
